package ar.edu.itba.it.atlc;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;

import ar.edu.itba.it.atlc.exception.InputException;
import ar.edu.itba.it.atlc.exception.InvalidTapeException;
import ar.edu.itba.it.atlc.model.MovementTransition;
import ar.edu.itba.it.atlc.model.Square;
import ar.edu.itba.it.atlc.model.Status;
import ar.edu.itba.it.atlc.model.Transition;
import ar.edu.itba.it.atlc.model.Transition.Movement;
import ar.edu.itba.it.atlc.model.Transition.Symbol;
import ar.edu.itba.it.atlc.model.WriteTransition;

public class Mt {

	private final Map<Status, List<Transition>> transitions;
	private Square head;
	private Status state;

	public Mt(final Map<Status, List<Transition>> transitions, Status initial) {
		this.transitions = transitions;
		state = initial;
	}

	public void start() {
		System.out.println("Ingrese la cinta a utilizar:");
		BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
		StringBuffer tapeInput = new StringBuffer();
		try {
			int aux;
			while((aux = stdin.read()) != -1) {
				tapeInput.append((char) aux);
			}
		} catch (IOException e) {
			throw new InputException("Error al leer los caracteres ingresados.");
		}
		head = createTape(tapeInput.toString());
		run();
	}

	private Square createTape(String tapeString) {
		Square head;
		if (tapeString.length() == 0) {
			head = new Square(Symbol.Symbol_0);
			return head;
		}
		char[] tapeCharArray = tapeString.toCharArray();
		try {
			head = new Square(Symbol.parse(String.valueOf(tapeCharArray[0])));
			Square aux = head;
			tapeCharArray = tapeString.substring(1).toCharArray();
			for (char c : tapeCharArray) {
				aux = aux.addRightSquare(Symbol.parse(String.valueOf(c)));
			}
		} catch (Exception e) {
			throw new InvalidTapeException("Cinta inválida.");
		}
		return head;
	}

	private void run() {
		Transition t;
		while ((t = getTransition()) != null) {
			state = t.getDestination();
			if (t.getClass() == WriteTransition.class) {
				head.write(((WriteTransition) t).getValue());
			} else if (t.getClass() == MovementTransition.class) {
				if (((MovementTransition) t).getMovement()
						.equals(Movement.LEFT)) {
					head = head.moveLeft();
				} else if (((MovementTransition) t).getMovement().equals(
						Movement.RIGHT)) {
					head = head.moveRight();
				}
			}
		}
		printTape(head);
	}

	private Transition getTransition() {
		for (Transition t : transitions.get(state)) {
			if (t.getSymbol().equals(head.getValue())) {
				return t;
			}
		}
		return null;
	}

	private void printTape(Square sq) {
		System.out.println("\n");
		while (sq.hasLeft()) {
			sq = sq.moveLeft();
		}
		System.out.print(sq.getValue());
		while (sq.hasRight()) {
			sq = sq.moveRight();
			System.out.print(sq.getValue());
		}
		System.out.println("");
	}

	public static void printStatus(Map<Status, List<Transition>> map) {
		for (Status s : map.keySet()) {
			System.out.println(s);
			for (Transition t : map.get(s)) {
				if (t.getClass() == MovementTransition.class) {
					System.out.println("MovementT : " + t);
				} else if (t.getClass() == WriteTransition.class) {
					System.out.println("WriteT : " + t);
				}
			}
		}
	}

}
